package cn.wps.moffice.main.local.filebrowser.operator.seek;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import android.os.Message;
import cn.wps.moffice.main.local.filebrowser.operator.fscache.DataCenter;

/**
 * FsCacheFinder 搜索者.
 * 仅用于 本地目录/所有文档功能模块 的 搜索组件.
 * ----------------------------------------------------------
 * 组件主要流程:
 * ----------------------------------------------------------
 * 对外提供 OnScan OnFresh OnSeek 三个入口, 经过初始化后,
 * 1. 首先调用 初始化
 * 2. 其次调用          startLoadTimer() 定时刷新,
 *              通过 seekHandler 通知View进行数据更新.
 * 3. 调用 各自线程  startScanThread()
 *              startFreshThread()
 *              startSeekThread().
 *
 * SeekHandler 参数说明
 * arg1: token， 通过与token对比, 判断是否合法.
 * arg2: index
 * obj:  seekType 哪种类型文档的搜索(MSG_FORCE_STOP除外)
 *
 * ----------------------------------------------------------
 *
 * @since 2014-8-18
 * @author wangxiaohua
 *
 */
public class FsFinder extends AbstractFinder
{
	private static final String ALLDOC_THREAD_NAME = "AllDocFinderThread";
	private static final String BROWSER_THREAD_NAME = "BrowserFinderThread";

	// private SparseBooleanArray mMediaQueryOver = new SparseBooleanArray();
	private ScheduledThreadPoolExecutor mloadTimer;

	private static final int LOAD_NOTIFY_INTERVAL = 200; // 开始刷的延时
	private static final int LOAD_NOTIFY_DURING = 500; //定时刷的间隔

	private AtomicInteger mThreadCount = new AtomicInteger();

	/**
	 * 所有文档的线程是否正在执行
	 */
	private boolean isAllDocThreadRunning;

	/**
	 * 本地目录搜索的线程是否正在执行，以及正在搜索的是哪一个目录
	 */
	private SeekFolderParam mSeekFolderParam;
	
	private class SeekFolderParam
	{
		String path;
		boolean seekPicture;
		boolean seekAll;
		
		public SeekFolderParam(String path, boolean seekPicture, boolean seekAll)
		{
			this.path = path;
			this.seekPicture = seekPicture;
			this.seekAll = seekAll;
		}
		
		public boolean equals(String path, boolean seekPicture, boolean seekAll)
		{
			if (this.seekPicture != seekPicture)
			{
				return false;
			}
			
			if (this.seekAll != seekAll)
			{
				return false;
			}
			
			if (null == this.path && null == path)
				return true;
			
			if (null == this.path || null == path || (!this.path.equals(path)))
			{
				return false;
			}
			
			return true;
		}
	}

	private static FsFinder mInstance;

	public synchronized static FsFinder getInstance()
	{
		if (mInstance == null)
		{
			mInstance = new FsFinder();
		}

		return mInstance;
	}

	/*
	 * 初始化 1. 搜索的根路径; 2. 是否搜索媒体库; 3. 搜索集合是否变化.
	 */
	private void reset(Set<File> seekRootFlods)
	{
		if (android.os.Build.VERSION.SDK_INT < 11)
		{
			addExtraFlodIn2XSeries(seekRootFlods);
		}
	}

	private void onThreadCreated() {
		mThreadCount.incrementAndGet(); //记录线程数
	}

	private void onThreadFinished() {
		int threadCount = mThreadCount.get();

		//最后一个线程结束，并且上层没有监听，可以清除数据中心数据
		if (threadCount == 0 && getCallbacks().size() == 0) {
			DataCenter.getInstance().resetAllPool();
		}
	}

	private void onRemoveCallbacks() {
		onThreadFinished();
	}

	@Override
	public void removeSeekCallback(SeekCallback callback) {
		super.removeSeekCallback(callback);
		onRemoveCallbacks();
	}

	public void find(SeekCallback callback)
	{
		addSeekCallback(callback);

		if (isAllDocThreadRunning)
		{
			sendForceFreshMsg(SeekType.OnFresh);
			return;
		}

		seekTime = System.currentTimeMillis();

		final Set<File> seekRootFlods = new HashSet<File>();
		reset(seekRootFlods);

		isAllDocThreadRunning = true;
		onThreadCreated();
		new Thread(ALLDOC_THREAD_NAME)
		{
			@Override
			public void run()
			{
				startFreshTimer(SeekType.OnFresh, false);
				
				//深度搜索
				fs.onFresh();

				seekOver(false, SeekType.OnFresh);
				
				isAllDocThreadRunning = false;
				mThreadCount.decrementAndGet();
				onThreadFinished();
			}
		}.start();
	}

	public void findCurrent(final String folderPath, final boolean seekPicture, final boolean seekAll, SeekCallback callback)
	{
		if (!seekPicture && seekAll)
		{
			find(callback);
			return;
		}
		
		addSeekCallback(callback);

		if (mSeekFolderParam != null && mSeekFolderParam.equals(folderPath, seekPicture, seekAll))
		{
			sendForceFreshMsg(SeekType.SeekCurrent);
			return;
		}

		seekTime = System.currentTimeMillis();

		final Set<File> seekRootFlods = new HashSet<File>();
		reset(seekRootFlods);

		mSeekFolderParam = new SeekFolderParam(folderPath, seekPicture, seekAll);
		
		onThreadCreated();
		new Thread(BROWSER_THREAD_NAME)
		{
			@Override
			public void run()
			{
				//搜索图片时插入到媒体库里，然后再统一一次性取出来
				if (!seekPicture)
				{
					startFreshTimer(SeekType.SeekCurrent, seekPicture);
				}

				//深度搜索
				fs.onSeekCurrent(folderPath, seekPicture, seekAll);

				seekOver(false, SeekType.SeekCurrent);
				
				mSeekFolderParam = null;
				mThreadCount.decrementAndGet();
				onThreadFinished();
			}
		}.start();
	}
	
	public void insertFile(String filePath)
	{
		File file = new File(filePath);
		fs.insertFile(file, file.getName());
	}

	/*
	 * 通知  View/Controlelr层 刷新.
	 */
	private void sendFreshMsg(SeekType seekType, int dirtyFlag)
	{
		 Message msg = mSeekHandler.obtainMessage();
		 msg.what = MSG_REFLESH;
		 msg.arg1 = dirtyFlag;
		 msg.obj = seekType;
		 msg.sendToTarget();
	}

	private void sendForceFreshMsg(SeekType seekType)
	{
		Message msg = mSeekHandler.obtainMessage();
		 msg.what = MSG_FORCE_REFRESH;
		 msg.arg1 = 0;
		 msg.arg2 = 0;
		 msg.obj = seekType;
		 msg.sendToTarget();
	}

	/*
	 * 添加根搜索目录.
	 */
	private void addRootDir(String filePath, Set<File> rootDir)
	{
		if (filePath != null)
		{
			File root = new File(filePath);
			if(root.exists() && root.isDirectory())
			{
				rootDir.add(root);
			}
		}
	}

	/*
	 * 添加额外的搜索根目录, 因为在 2.x系统设备上，改媒体搜索为这些指定目录.
	 */
	private void addExtraFlodIn2XSeries(Set<File> rootDir)
	{
		//search special files ,such as my document, download.
		//TODO Demo屏蔽
//		addRootDir(OfficeApp.getInstance().newDocumentPath, rootDir);
//		addRootDir(OfficeApp.getInstance().sdcardPath + DOWNLOADPATH, rootDir);
	}

	/*
	 * 启动 定时消息线程：通过View/ Controller 进行数据更新.
	 */
	private void startFreshTimer( SeekType seekType, boolean seekPicture)
	{
		Runnable task = null;
		if (seekPicture)
		{
			task = getPictureRunner(seekType);
		}
		else
		{
			task = getFreshRunner(seekType);
		}
		
		if (null == mloadTimer)
		{
			mloadTimer = new ScheduledThreadPoolExecutor(1);
			try
			{
				// 200ms后执行刷新, 然后每隔200ms进行一次刷新流程.
				// mloadTimer.scheduleAtFixedRate(task, LOAD_NOTIFY_INTERVAL, LOAD_NOTIFY_INTERVAL, TimeUnit.MILLISECONDS);
				mloadTimer.scheduleAtFixedRate(task, LOAD_NOTIFY_INTERVAL, LOAD_NOTIFY_DURING, TimeUnit.MILLISECONDS);
			}
			catch(RejectedExecutionException e)
			{
				seekOver(true, seekType);
			}
		}
	}

	private Runnable getFreshRunner(final SeekType seekType)
	{
		return new Runnable()
		{
			@Override
			public void run()
			{
				boolean needRefresh = false;

				int dirtyFlag = DataCenter.getInstance().getDirtFlag();
				if (dirtyFlag != 0)
				{
					DataCenter.getInstance().clearDirtyAll();
					needRefresh = true;
				}

				if (needRefresh)
					sendFreshMsg(seekType, dirtyFlag);
			}
		};
	}
	
	//图片的搜索比较特殊，因为我们现在没有支持脏标记，所以每次都刷新
	private Runnable getPictureRunner(final SeekType seekType)
	{
		return new Runnable()
		{
			@Override
			public void run()
			{
				sendFreshMsg(seekType, Integer.MAX_VALUE);
			}
		};
	}

	// FIXME: token考虑进行清空处理, 由于子线程无法即时退出, 其他数据的清空, 放入 FS与DC进行 dispose.
	public void dispose()
	{
		// 阻止加载.
		stopTimer();
	}

	private void seekOver(boolean isForceStop, SeekType seekType)
	{
		if (!isForceStop)
		{
			stopTimer();
			Message msg = mSeekHandler.obtainMessage();
			msg.what = MSG_FINISH;
			msg.arg1 = 0;
			msg.arg2 = 0;
			msg.obj = seekType;
			msg.sendToTarget();
		}
	}

	// 停止 装载器.
	private void stopTimer()
	{
		if (null != mloadTimer)
		{
			mloadTimer.shutdownNow();
			mloadTimer = null;
		}
	}
}
